﻿using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Drawing;
using System.Drawing.Imaging;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib;
using System.IO.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace SNSHelper.Common
{
    public struct SystemTime   
    {   
        public ushort wYear;   
        public ushort wMonth;   
        public ushort wDayOfWeek;   
        public ushort wDay;   
        public ushort wHour;   
        public ushort wMinute;   
        public ushort wSecond;   
        public ushort wMilliseconds;   

        /// <summary>   
        /// 从System.DateTime转换。   
        /// </summary>   
        /// <param name="time">System.DateTime类型的时间。</param>   
        public void FromDateTime(DateTime time)   
        {
            wYear = (ushort)time.Year;   
            wMonth = (ushort)time.Month;   
            wDayOfWeek = (ushort)time.DayOfWeek;   
            wDay = (ushort)time.Day;   
            wHour = (ushort)time.Hour;   
            wMinute = (ushort)time.Minute;   
            wSecond = (ushort)time.Second;   
            wMilliseconds = (ushort)time.Millisecond;   
        }   
        /// <summary>   
        /// 转换为System.DateTime类型。   
        /// </summary>   
        /// <returns></returns>   
        public DateTime ToDateTime()   
        {   
            return new DateTime(wYear, wMonth, wDay, wHour, wMinute, wSecond, wMilliseconds);   
        }   
        /// <summary>   
        /// 静态方法。转换为System.DateTime类型。   
        /// </summary>   
        /// <param name="time">SYSTEMTIME类型的时间。</param>   
        /// <returns></returns>   
        public static DateTime ToDateTime(SystemTime time)   
        {   
            return time.ToDateTime();   
        }   
    }  

    public class HttpHelper
    {
        #region 私有变量
        private CookieContainer cc;
        //private string contentType = "application/x-www-form-urlencoded";
        //private string contentType = "text/html; charset=utf-8";
        private string innerContentType = "application/x-www-form-urlencoded";
        //private string accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/x-silverlight, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-silverlight-2-b1, */*";
        //private string userAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)";
        private string accept = "text/html, application/xhtml+xml, */*";
        private string userAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)";
        private Encoding encoding = Encoding.GetEncoding("utf-8");        
        //private Encoding encoding = Encoding.ASCII;

        private int delay = 1000;
        private int maxTry = 3;
        private string strProxyAddress = string.Empty;
        //private string strProxyAddress = "61.172.244.108:80";
        private string strProxyUser = string.Empty;
        private string strProxyPwd = string.Empty;
        private string strProxyBypass = string.Empty;
        private bool bGZip = true;
        private WebProxy webProxy = null;
        #endregion

        #region 属性
        /// <summary>
        /// Cookie容器
        /// </summary>
        public CookieContainer cookieContainer
        {
            get
            {
                return cc;
            }
            set
            {
                cc = value;
            }
        }

        /// <summary>
        /// Cookie容器
        /// </summary>
        /// 
        public string ProxyAddress
        {
            get
            {
                return strProxyAddress;
            }

            set
            {
                strProxyAddress = value;
                webProxy = GetWebProxy();
            }
        }

        public string ProxyUser
        {
            get
            {
                return strProxyUser;
            }

            set
            {
                strProxyUser = value;
            }
        }

        public string ProxyPwd
        {
            get
            {
                return strProxyPwd;
            }

            set
            {
                strProxyPwd = value;
            }
        }

        /// <summary>
        /// 获取网页源码时使用的编码
        /// </summary>
        /// <value></value>
        public Encoding Encoding
        {
            get
            {
                return encoding;
            }
            set
            {
                encoding = value;
            }
        }

        public int NetworkDelay
        {
            get
            {
                return delay;
            }
            set
            {
                delay = value;
            }
        }

        public int MaxTry
        {
            get
            {
                return maxTry;
            }
            set
            {
                maxTry = value;
            }
        }
        #endregion

        #region 构造函数
        /// <summary>
        /// Initializes a new instance of the <see cref="HttpHelper"/> class.
        /// </summary>
        public HttpHelper()
        {
            cc = new CookieContainer();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HttpHelper"/> class.
        /// </summary>
        /// <param name="cc">The cc.</param>
        public HttpHelper(CookieContainer cc)
        {
            this.cc = cc;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HttpHelper"/> class.
        /// </summary>
        /// <param name="contentType">Type of the content.</param>
        /// <param name="accept">The accept.</param>
        /// <param name="userAgent">The user agent.</param>
        public HttpHelper(string contentType, string accept, string userAgent)
        {
            this.innerContentType = contentType;
            this.accept = accept;
            this.userAgent = userAgent;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HttpHelper"/> class.
        /// </summary>
        /// <param name="cc">The cc.</param>
        /// <param name="contentType">Type of the content.</param>
        /// <param name="accept">The accept.</param>
        /// <param name="userAgent">The user agent.</param>
        public HttpHelper(CookieContainer cc, string contentType, string accept, string userAgent)
        {
            this.cc = cc;
            this.innerContentType = contentType;
            this.accept = accept;
            this.userAgent = userAgent;
        }

        public HttpHelper(CookieContainer cc, string contentType, string accept, string userAgent, bool gzip)
        {
            this.cc = cc;
            this.innerContentType = contentType;
            this.accept = accept;
            this.userAgent = userAgent;
            this.bGZip = gzip;
        }

        public void SignOut()
        {
            this.cookieContainer = new CookieContainer();
        }

        #endregion

        #region 公共方法

        private WebProxy GetWebProxy()
        {
            // 需要使用Proxy和其配置
            if (this.strProxyAddress.Length > 0)
            {
                if (this.strProxyAddress == "DEFAULTPROXY")
                {
                    return WebProxy.GetDefaultProxy();
                }
                else
                {
                    WebProxy loProxy = new WebProxy(strProxyAddress, true);
                    if (this.strProxyBypass.Length > 0)
                    {
                        loProxy.BypassList = this.strProxyBypass.Split(';');
                    }

                    if (this.strProxyUser.Length > 0)
                    {
                        loProxy.Credentials = new NetworkCredential(this.strProxyUser, this.strProxyPwd);
                    }
                    else
                    {
                        loProxy.Credentials = CredentialCache.DefaultCredentials;
                    }

                    return loProxy;
                }
            }

            return null;
        }

        /// <summary>
        /// 获取指定页面的HTML代码
        /// </summary>
        /// <param name="url">指定页面的路径</param>
        /// <returns></returns>
        public string GetHtml(string url)
        {
            return GetHtml(url, string.Empty, string.Empty, false, cc, this.innerContentType, this.bGZip);
        }

        public string GetHtmlXml(string url, bool gzip)
        {
            return System.Text.RegularExpressions.Regex.Replace(GetHtml(url, string.Empty, string.Empty, false, cc, "application/xml", gzip), "^[^<]", "");
        }

        /// <summary>
        /// 获取指定页面的HTML代码
        /// </summary>
        /// <param name="url">指定页面的路径</param>
        /// <param name="postData">回发的数据</param>
        /// <param name="isPost">是否以post方式发送请求</param>
        /// <returns></returns>
        public string GetHtml(string url, string postData, bool isPost)
        {
            return GetHtml(url, string.Empty, postData, isPost, cc, this.innerContentType, this.bGZip);
        }

        public string GetHtml(string url, string referer, string postData, bool isPost)
        {
            return GetHtml(url, referer, postData, isPost, cc, this.innerContentType, this.bGZip);
        }

        public bool DownLoadFile(string strUrl, string strFilePath)
        {
            try
            {
                if (File.Exists(strFilePath))
                {
                    File.Delete(strFilePath);
                }

                WebClient wc = new WebClient();
                wc.DownloadFile(strUrl, strFilePath);
                return true;
            }
            catch (System.Exception)
            {
                return false;
            }
        }

        public Image LoadImage(string strUrl)
        {
            if (strUrl.Length == 0)
            {
                return null;
            }

            try
            {
                WebClient wc = new WebClient();
                Stream stream = wc.OpenRead(strUrl);
                if (stream == null)
                {
                    return null;
                }
                return Image.FromStream(stream);
            }
            catch (System.Exception)
            {
                return null;
            }
        }

        public bool GetSystemTime(ref DateTime date)
        {
            // 记录开始的时间   
            DateTime startDT = DateTime.Now;

            //建立IPAddress对象与端口，创建IPEndPoint节点:   
            int port = 13;
            string[] whost = { "time-nw.nist.gov"/*, "time-a.nist.gov", "5time.nist.gov", "time-b.nist.gov", "tick.mit.edu", "time.windows.com", "clock.sgi.com" */};

            IPHostEntry iphostinfo = Dns.GetHostEntry("131.107.13.100");
            IPAddress ip = iphostinfo.AddressList[0];
            IPEndPoint ipe = new IPEndPoint(ip, port);
            Socket c = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建Socket

            c.ReceiveTimeout = 2 * 1000;//设置超时时间   

            string sEX = "";// 接受错误信息   

            // 遍历时间服务器列表   
            foreach (string strHost in whost)
            {
                try
                {
                    iphostinfo = Dns.GetHostEntry(strHost);
                    ip = iphostinfo.AddressList[0];
                    ipe = new IPEndPoint(ip, port);

                    c.Connect(ipe);//连接到服务器   
                    if (c.Connected) break;// 如果连接到服务器就跳出   
                }
                catch (Exception ex)
                {
                    sEX = ex.Message;
                }
            }

            if (!c.Connected)
            {
                return false;
            }

            //SOCKET同步接受数据   
            byte[] RecvBuffer = new byte[1024];
            int nBytes, nTotalBytes = 0;
            StringBuilder sb = new StringBuilder();
            System.Text.Encoding myE = Encoding.UTF8;

            while ((nBytes = c.Receive(RecvBuffer, 0, 1024, SocketFlags.None)) > 0)
            {
                nTotalBytes += nBytes;
                sb.Append(myE.GetString(RecvBuffer, 0, nBytes));
            }
            //关闭连接   

            c.Close();

            int iCnt = 0;
            while(sb.Length == 0)
            {
                if (iCnt > 5)
                {
                    return false;
                }
                c = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建Socket
                c.ReceiveTimeout = 2000;

                c.Connect(ipe);//连接到服务器
                if (c.Connected)                   
                {
                    while ((nBytes = c.Receive(RecvBuffer, 0, 1024, SocketFlags.None)) > 0)
                    {
                        nTotalBytes += nBytes;
                        sb.Append(myE.GetString(RecvBuffer, 0, nBytes));
                    }
                }
                else
                {
                    return false;
                }
                c.Close();
                ++iCnt;
            }
            //关闭连接   
            c.Close();

            string[] o = sb.ToString().Split(' '); // 打断字符串   

            TimeSpan k = new TimeSpan();
            k = (TimeSpan)(DateTime.Now - startDT);// 得到开始到现在所消耗的时间

            if (o.Length < 3)
            {
                return false;
            }

            DateTime SetDT = Convert.ToDateTime(o[1] + " " + o[2]).Subtract(-k);// 减去中途消耗的时间   

            //处置北京时间 +8时   
            SetDT = SetDT.AddHours(8);

            date = SetDT;

            return true;
        }

        private string GetHtml(string strUrl, string strReferer, string strPostData, bool isPost, CookieContainer cookieContainer, string contentType, bool gzip)
        {
            HttpWebRequest httpWebRequest = null;
            HttpWebResponse httpWebResponse = null;
            Stream stream = null;
            Stream responseStream = null;
            StreamReader streamReader = null;

            try
            {
                httpWebRequest = (HttpWebRequest)WebRequest.Create(strUrl);
                httpWebRequest.CookieContainer = cookieContainer;
                httpWebRequest.ContentType = contentType;
                httpWebRequest.Accept = accept;
                httpWebRequest.UserAgent = userAgent;

                if (string.IsNullOrEmpty(strReferer) == false)
                {
                    httpWebRequest.Referer = strReferer;
                }

                httpWebRequest.Method = isPost ? "POST" : "GET";
                httpWebRequest.KeepAlive = true;

                if (gzip == true)
                {
                    httpWebRequest.Headers.Add("Accept-Language", "zh-CN");
                    httpWebRequest.Headers.Add("Accept-Encoding", "gzip, deflate");
                }

                // 需要安全验证的访问
                if (string.IsNullOrEmpty(this.strProxyAddress) == false)
                {
                    if (this.strProxyAddress == "AUTOLOGIN")
                        httpWebRequest.Credentials = CredentialCache.DefaultCredentials;
                    else
                        httpWebRequest.Credentials = new NetworkCredential(this.strProxyUser, this.strProxyPwd);
                }

                // 需要使用Proxy和其配置
                if ((strProxyAddress.Length > 0) && (webProxy == null))
                {
                    webProxy = GetWebProxy();
                }

                if (webProxy != null)
                {
                    httpWebRequest.Proxy = webProxy;
                }

                if (isPost)
                {
                    if (string.IsNullOrEmpty(strPostData) == true)
                    {
                        strPostData = string.Empty;
                    }

                    byte[] byteRequest = encoding.GetBytes(strPostData);
                    httpWebRequest.ContentLength = byteRequest.Length;
                    stream = httpWebRequest.GetRequestStream();
                    stream.Write(byteRequest, 0, byteRequest.Length);
                    stream.Close();
                }

                httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
                cookieContainer.Add(httpWebResponse.ResponseUri, httpWebResponse.Cookies);
                responseStream = this.bGZip == true ? Gzip(httpWebResponse) : httpWebResponse.GetResponseStream();
                streamReader = new StreamReader(responseStream, encoding);
                string html = streamReader.ReadToEnd();

                return html;
            }
            catch (WebException ex)
            {
                httpWebResponse = (HttpWebResponse)ex.Response;
                cookieContainer.Add(httpWebResponse.Cookies);
                responseStream = this.bGZip == true ? Gzip(httpWebResponse) : httpWebResponse.GetResponseStream();
                streamReader = new StreamReader(responseStream, encoding);
                string html = streamReader.ReadToEnd();
                return html;
            }
            catch (Exception e)
            {
                if (e.Message == "The remote name could not be resolved: 'm.kaixin.com'"
                    || (e.Message == "The operation has timed out")
                    || (e.Message == "Unable to connect to the remote server")
                    || (e.Message.Contains("Unable to read data") == true)
                    )
                {
                    Thread.Sleep(5000);
                    return GetHtml(strUrl, strReferer, strPostData, isPost, cookieContainer, contentType, gzip);
                }
                else
                {
                    return string.Empty;
                }
            }
            finally
            {
                if (stream != null)
                {
                    stream.Close();
                }

                if (streamReader != null)
                {
                    streamReader.Close();
                }

                if (responseStream != null)
                {
                    responseStream.Close();
                }

                if (httpWebResponse != null)
                {
                    httpWebResponse.Close();
                }

                if (httpWebRequest != null)
                {
                    httpWebRequest.Abort();
                }
            }
        }

        public string HttpPostFormData(string url, string strReferer, Dictionary<string, string> stringDict/*, string fileKeyName, string filePath = ""*/)
        {
            HttpWebRequest httpWebRequest = null;
            HttpWebResponse httpWebResponse = null;
            FileStream fileStream = null;
            Stream responseStream = null;
            Stream requestStream = null;
            StreamReader streamReader = null;

            try
            {
                httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
                httpWebRequest.CookieContainer = cookieContainer;
                httpWebRequest.Accept = accept;
                httpWebRequest.Referer = strReferer;
                httpWebRequest.UserAgent = userAgent;
                httpWebRequest.AllowAutoRedirect = false;

                if (this.bGZip == true)
                {
                    httpWebRequest.Headers.Add("Accept-Language", "zh-CN");
                    httpWebRequest.Headers.Add("Accept-Encoding", "gzip, deflate");
                }
                httpWebRequest.Method = "POST";

                // 边界符
                var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
                httpWebRequest.ContentType = "multipart/form-data; boundary=" + boundary;
                httpWebRequest.KeepAlive = true;
                //httpWebRequest.Headers.Add("Connection", "Keep-Alive");
                httpWebRequest.Headers.Add("Cache-Control", "no-cache");
                //-----------------------------
                // 边界符
                string beginBoundary = "--" + boundary + "\r\n";
                // 最后的结束符
                string endBoundary = "--" + boundary + "--\r\n";

                /*
                // 写入文件
                const string filePartHeader =
                    "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n" +
                        "Content-Type: application/octet-stream\r\n\r\n";
                var header = string.Format(filePartHeader, fileKeyName, filePath);
                var headerbytes = Encoding.UTF8.GetBytes(header);

                memStream.Write(beginBoundary, 0, beginBoundary.Length);
                memStream.Write(headerbytes, 0, headerbytes.Length);

                if (string.IsNullOrEmpty(filePath) == false &&
                        string.IsNullOrEmpty(fileKeyName) == false)
                {
                    var buffer = new byte[1024];
                    int bytesRead; // =0
                    fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);

                    while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                    {
                        memStream.Write(buffer, 0, bytesRead);
                    }
                }
                 * */

                string strPostData = "";

                // 写入字符串的Key
                string stringKeyHeader = "--" + boundary +
                                       "\r\nContent-Disposition: form-data; name=\"{0}\"" +
                                       "\r\n\r\n{1}\r\n";
                string filePartHeader = "--" + boundary +
                                       "\r\nContent-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n" +
                                        "Content-Type: application/octet-stream\r\n\r\n\r\n";

                foreach (string var in from string key in stringDict.Keys
                                       select key.Contains("[file]") == false ? string.Format(stringKeyHeader, key, stringDict[key]) : string.Format(filePartHeader, key, stringDict[key]))
                {
                    strPostData += var;
                }

                strPostData += endBoundary;

                byte[] byteRequest = encoding.GetBytes(strPostData);
                httpWebRequest.ContentLength = byteRequest.Length;

                requestStream = httpWebRequest.GetRequestStream();
                requestStream.Write(byteRequest, 0, byteRequest.Length);
                requestStream.Close();

                httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
                cookieContainer.Add(httpWebResponse.ResponseUri, httpWebResponse.Cookies);
                responseStream = this.bGZip == true ? Gzip(httpWebResponse) : httpWebResponse.GetResponseStream();
                streamReader = new StreamReader(responseStream, encoding);
                string html = streamReader.ReadToEnd();

                return html;
            }
            catch (WebException ex)
            {
                httpWebResponse = (HttpWebResponse)ex.Response;
                cookieContainer.Add(httpWebResponse.Cookies);
                responseStream = this.bGZip == true ? Gzip(httpWebResponse) : httpWebResponse.GetResponseStream();
                streamReader = new StreamReader(responseStream, encoding);
                string html = streamReader.ReadToEnd();
                return html;
            }
            catch (Exception)
            {
                return string.Empty;
            }
            finally
            {
                if (fileStream != null)
                {
                    fileStream.Close();
                }

                if (streamReader != null)
                {
                    streamReader.Close();
                }

                if (responseStream != null)
                {
                    responseStream.Close();
                }

                if (httpWebResponse != null)
                {
                    httpWebResponse.Close();
                }

                if (httpWebRequest != null)
                {
                    httpWebRequest.Abort();
                }
            }
        }

        //解密
        private Stream Gzip(HttpWebResponse HWResp)
        {
            Stream stream1 = null;

            if (HWResp.ContentEncoding == "gzip")
            {
                stream1 = new GZipInputStream(HWResp.GetResponseStream());
            }
            else
            {
                if (HWResp.ContentEncoding == "deflate")
                {
                    stream1 = new InflaterInputStream(HWResp.GetResponseStream());
                }
            }

            if (stream1 == null)
            {
                return HWResp.GetResponseStream();
            }

            MemoryStream stream2 = new MemoryStream();
            int count = 0x800;
            byte[] buffer = new byte[0x800];

            while (true)
            {
                count = stream1.Read(buffer, 0, count);

                if (count <= 0)
                {
                    break;
                }

                stream2.Write(buffer, 0, count);
            }

            stream2.Seek((long)0, SeekOrigin.Begin);
            return stream2;
        }

        #endregion
    }
}
